Skip to content

Update surf-liquid TVL adapter with V2/V3 vault support and staking#18401

Merged
RohanNero merged 5 commits intoDefiLlama:mainfrom
rohansingh4:update-surf-liquid-tvl
Mar 27, 2026
Merged

Update surf-liquid TVL adapter with V2/V3 vault support and staking#18401
RohanNero merged 5 commits intoDefiLlama:mainfrom
rohansingh4:update-surf-liquid-tvl

Conversation

@rohansingh4
Copy link
Copy Markdown
Contributor

@rohansingh4 rohansingh4 commented Mar 11, 2026

Summary

  • Update TVL adapter to dynamically enumerate V2 vaults via factory (getTotalVaults/getVaultInfo) and V3 vaults via VaultDeployed event logs
  • Count Morpho ERC-4626 vault share positions held by each vault as TVL
  • Add staking section: SURF staked in SURFStakingV5 + CreatorBid SURF subscriptions (SURF locked in the token contract)

Test results

  • TVL: ~$239k (Morpho vault deposits across V2 + V3 vaults)
  • Staking: ~$31k (SURF staked + CreatorBid subscriptions)

Contracts

  • V2 Factory: 0x1D283b668F947E03E8ac8ce8DA5505020434ea0E (Base)
  • V3 Factory: 0xf1d64dee9f8e109362309a4bfbb523c8e54fa1aa (Base)
  • SURF Staking: 0xB0fDFc081310A5914c2d2c97e7582F4De12FA9d6 (Base)
  • SURF Token: 0xcdca2eaae4a8a6b83d7a3589946c2301040dafbf (Base)

Summary by CodeRabbit

  • New Features

    • Enhanced TVL tracking that discovers and aggregates positions across V2 and V3 vaults (replaces previous target-based approach).
    • SURF staking and creator subscription balances are now included in TVL reporting.
    • Expanded asset support including USDC, WETH and CBBTC.
  • Documentation

    • Methodology updated for multi-source TVL and staking; metadata now marks double-counting and adds a V3 factory hallmark.

- Enumerate V2 vaults via factory getTotalVaults/getVaultInfo
- Discover V3 vaults via VaultDeployed event logs
- Count Morpho ERC-4626 vault share positions as TVL
- Add staking: SURF staked + CreatorBid subscriptions
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d3d74e40-5f97-4ff7-9567-171cba319b72

📥 Commits

Reviewing files that changed from the base of the PR and between 241a0d7 and b0a4fd3.

📒 Files selected for processing (1)
  • projects/surf-liquid/index.js

📝 Walkthrough

Walkthrough

Introduces a new tvl(api) that separately discovers V2 vaults via a V2 factory and V3 vaults via VaultDeployed logs, aggregates token/owner pairs and calls api.sumTokens. Adds staking(api) (SURF totalStaked + SURF subscription balance) and updates exported metadata and hallmarks.

Changes

Cohort / File(s) Summary
Surf Liquid TVL & staking
projects/surf-liquid/index.js
Replaces prior inline TVL with a new tvl(api) that enumerates V2 vaults via V2_FACTORY (getTotalVaults/getVaultInfo) and discovers V3 vaults from VaultDeployed logs on V3_FACTORY, builds tokensAndOwners and calls api.sumTokens. Adds staking(api) reading SURF_STAKING.totalStaked and SURF_TOKEN.balanceOf(SURF_TOKEN) for subscriptions. Declares constants (V2_FACTORY, V3_FACTORY, SURF_STAKING, SURF_TOKEN, USDC, WETH, CBBTC, ASSETS, ZERO_ADDR), wires module.exports.base.tvl to tvl, adds base.staking, updates methodology, sets doublecounted: true, and adds hallmarks entry for V3 factory launch.

Sequence Diagram(s)

sequenceDiagram
    participant Client as API Handler
    participant V2F as V2 Factory
    participant V3F as V3 Factory (logs)
    participant Vault as Vault Contract
    participant Sum as api.sumTokens

    Client->>V2F: getTotalVaults() / getVaultInfo(idx)
    V2F-->>Client: V2 vault addresses & currentVault owners
    Client->>V3F: query VaultDeployed logs (from startBlock → latest)
    V3F-->>Client: V3 vault addresses (asset → vault)
    Client->>Vault: call token(), owner() for each vault
    Vault-->>Client: token / owner pairs
    Client->>Sum: call sumTokens({ tokensAndOwners })
    Sum-->>Client: aggregated TVL per token
Loading
sequenceDiagram
    participant Client as API Handler
    participant STK as SURF_STAKING
    participant SURF as SURF_TOKEN
    participant Acc as Aggregator

    Client->>STK: call totalStaked()
    STK-->>Client: staked SURF amount
    Client->>SURF: call balanceOf(SURF_TOKEN) (creator subscriptions)
    SURF-->>Client: subscription SURF amount
    Client->>Acc: add SURF balances to tokensAndOwners
    Acc-->>Client: SURF included in final TVL/staking
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I'm a rabbit counting vaults by moonlit beams,
Hopping through V2 gardens and V3 streams,
I gather SURF stakes and subscription cheer,
Tokens and owners — carrots lined near,
A tiny hop for code, a big nibble of dreams. 🥕🐇

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: updating the surf-liquid TVL adapter to support both V2 and V3 vaults and adding staking functionality.
Description check ✅ Passed The PR description provides a clear summary of changes, test results with specific metrics, and contract addresses. However, it does not follow the provided repository template structure, which requires new listings to include metadata fields like methodology, token address, and category.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@llamabutler
Copy link
Copy Markdown

The adapter at projects/surf-liquid exports TVL:

base                      239.21 k
base-staking              31.42 k
staking                   31.42 k

total                    239.21 k 

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@projects/surf-liquid/index.js`:
- Around line 71-76: The exported adapter object (module.exports) currently
exposes base: { tvl, staking } where base.tvl reports Morpho ERC‑4626 share
balances held by Surf vaults; mark this as a wrapped/downstream TVL by adding
doublecounted: true and misrepresentedTokens: true to the exported object (next
to methodology and base) so consumers know it’s vault‑in‑vault/wrapped-share
TVL; alternatively, if you prefer to represent underlying assets instead, modify
the tvl implementation referenced by the tvl symbol to unwrap ERC‑4626 shares to
their underlying before summing, but the quick fix is to add the two flags to
module.exports alongside base and methodology.
- Around line 25-27: The V2 loop pushes [v2MorphoVaults[i], v2Owners[i]] even
when one of those values is falsy; mirror the V3 guard by checking both the
vault address and owner are truthy and not ZERO_ADDR before pushing to
tokensAndOwners. Update the loop that iterates v2Owners / v2MorphoVaults to skip
iterations where v2MorphoVaults[i] is ZERO_ADDR or where v2MorphoVaults[i] or
v2Owners[i] are falsy (e.g., result of getVaultInfo()/currentVault()), so
sumTokens only receives valid [token, owner] pairs. Ensure you reference the
same variables (v2MorphoVaults, v2Owners, ZERO_ADDR, tokensAndOwners, sumTokens)
when making the check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9bb8de9-d9d4-4001-8461-aff70cf9d3e1

📥 Commits

Reviewing files that changed from the base of the PR and between 231d331 and f124e99.

📒 Files selected for processing (1)
  • projects/surf-liquid/index.js

@rohansingh4
Copy link
Copy Markdown
Contributor Author

Revenue is 70,000 Surf tokens, you can convert them to USD. Please refer- https://dune.com/surfliquid/stats, tvl, volume, surf buyback (is our revenue) here should't divert much from dune numbers. Dune also tracks historical prices, so I am aware there will be slight variation though most of the volume/tvl is in usdc.

Include SURF staking and CreatorBid subscriptions directly in
the tvl function instead of a separate staking category.
@llamabutler
Copy link
Copy Markdown

The adapter at projects/surf-liquid exports TVL:

base                      270.91 k

total                    270.91 k 

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
projects/surf-liquid/index.js (1)

56-67: Consider separating staking into its own export.

SURF staking and CreatorBid subscriptions are currently merged into the tvl function. DefiLlama convention typically separates protocol token staking into a dedicated staking export. This provides clearer metrics and matches the test output showing distinct base ($239k) and base-staking ($31k) values.

♻️ Suggested refactor to separate staking
 async function tvl(api) {
   const tokensAndOwners = [];
   // ... V2 and V3 vault logic ...
   await api.sumTokens({ tokensAndOwners });
-
-  // SURF staking contract
-  const totalStaked = await api.call({ abi: "uint256:totalStaked", target: SURF_STAKING });
-  api.add(SURF_TOKEN, totalStaked);
-
-  // CreatorBid SURF subscriptions (SURF locked in the token contract)
-  const subscribed = await api.call({
-    abi: "function balanceOf(address) view returns (uint256)",
-    target: SURF_TOKEN,
-    params: [SURF_TOKEN],
-  });
-  api.add(SURF_TOKEN, subscribed);
 }

+async function staking(api) {
+  // SURF staking contract
+  const totalStaked = await api.call({ abi: "uint256:totalStaked", target: SURF_STAKING });
+  api.add(SURF_TOKEN, totalStaked);
+
+  // CreatorBid SURF subscriptions (SURF locked in the token contract)
+  const subscribed = await api.call({
+    abi: "function balanceOf(address) view returns (uint256)",
+    target: SURF_TOKEN,
+    params: [SURF_TOKEN],
+  });
+  api.add(SURF_TOKEN, subscribed);
+}

 module.exports = {
   // ...
   base: {
     tvl,
+    staking,
   },
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/surf-liquid/index.js` around lines 56 - 67, Split the SURF staking
logic out of the tvl function into a dedicated staking export: move the call
that reads totalStaked (the api.call with abi "uint256:totalStaked" targeting
SURF_STAKING) and the corresponding api.add(SURF_TOKEN, totalStaked) into a new
exported async function named staking (or export const staking = { tvl: ... }
per repo convention) that returns the staking balances; keep the existing
CreatorBid subscription code (the balanceOf call using SURF_TOKEN as target and
params [SURF_TOKEN] and its api.add) inside tvl. Ensure the new staking export
uses the same api instance pattern and is exported alongside tvl so
tests/reporting show separate base and base-staking values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@projects/surf-liquid/index.js`:
- Around line 56-67: Split the SURF staking logic out of the tvl function into a
dedicated staking export: move the call that reads totalStaked (the api.call
with abi "uint256:totalStaked" targeting SURF_STAKING) and the corresponding
api.add(SURF_TOKEN, totalStaked) into a new exported async function named
staking (or export const staking = { tvl: ... } per repo convention) that
returns the staking balances; keep the existing CreatorBid subscription code
(the balanceOf call using SURF_TOKEN as target and params [SURF_TOKEN] and its
api.add) inside tvl. Ensure the new staking export uses the same api instance
pattern and is exported alongside tvl so tests/reporting show separate base and
base-staking values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 84b8abd6-d866-4fd3-a80c-e0bf4684b494

📥 Commits

Reviewing files that changed from the base of the PR and between f124e99 and e90db2a.

📒 Files selected for processing (1)
  • projects/surf-liquid/index.js

@RohanNero RohanNero self-assigned this Mar 12, 2026

module.exports = {
methodology: "Counts the morpho deposits of each Surf Liquid vault.",
methodology: "TVL counts Morpho vault deposits across V2 and V3 Surf Liquid vaults, SURF staked, and CreatorBid SURF subscriptions.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a hallmarks entry for the v3 factory deployment date so the spike in tvl is explained

target: SURF_TOKEN,
params: [SURF_TOKEN],
});
api.add(SURF_TOKEN, subscribed);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move the SURF token tracking back under the staking export

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, also when will numbers be updated and depreciation tag removed?

Per reviewer feedback:
- Move SURF staking + CreatorBid subscriptions back under staking export
- Add hallmarks entry for V3 factory launch (2025-11-30) to explain TVL spike
@llamabutler
Copy link
Copy Markdown

The adapter at projects/surf-liquid exports TVL:

base                      239.11 k
base-staking              36.80 k
staking                   36.80 k

total                    239.11 k 

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
projects/surf-liquid/index.js (2)

71-77: ⚠️ Potential issue | 🟠 Major

Mark wrapped/downstream TVL explicitly in adapter metadata.

base.tvl sums Morpho ERC-4626 share balances held by Surf vaults, which is wrapped/downstream exposure and can overlap with underlying protocol TVL. Add doublecounted: true and misrepresentedTokens: true (or unwrap to underlying instead).

🏷️ Proposed metadata update
 module.exports = {
   methodology: "TVL counts Morpho vault deposits across V2 and V3 Surf Liquid vaults. Staking includes SURF staked and SURF subscriptions.",
+  doublecounted: true,
+  misrepresentedTokens: true,
   hallmarks: [["2025-11-30", "V3 factory launched"]],
   base: {
     tvl,
     staking,
   },
 };

Based on learnings, the Royco v2 adapter marks vault-in-vault accounting as doublecounted: true, and AFI marks raw vault-share reporting as misrepresentedTokens: true.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/surf-liquid/index.js` around lines 71 - 77, The adapter metadata
currently exports module.exports with base: { tvl, staking } but does not mark
that base.tvl reports wrapped/downstream Morpho ERC-4626 share balances; update
the exported metadata to explicitly flag this by adding doublecounted: true and
misrepresentedTokens: true (or alternatively unwrap the vault shares to
underlying assets) so consumers know the TVL may overlap with underlying
protocols; locate the module.exports block (symbols: module.exports, base, tvl,
staking) and add those two boolean flags to the metadata object.

25-27: ⚠️ Potential issue | 🟡 Minor

Harden V2 pair validation before sumTokens.

At Line 26, the guard still only checks v2MorphoVaults[i] !== ZERO_ADDR. If v2Owners[i] or v2MorphoVaults[i] is falsy, an invalid [token, owner] pair can still be pushed.

🛡️ Proposed fix
   for (let i = 0; i < v2Owners.length; i++) {
-    if (v2MorphoVaults[i] !== ZERO_ADDR) {
+    if (v2Owners[i] && v2Owners[i] !== ZERO_ADDR && v2MorphoVaults[i] && v2MorphoVaults[i] !== ZERO_ADDR) {
       tokensAndOwners.push([v2MorphoVaults[i], v2Owners[i]]);
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/surf-liquid/index.js` around lines 25 - 27, The loop that builds
tokensAndOwners currently only checks v2MorphoVaults[i] !== ZERO_ADDR and can
push invalid pairs when v2Owners[i] or v2MorphoVaults[i] is falsy; update the
guard in the loop that iterates over v2Owners to ensure both v2MorphoVaults[i]
is truthy and not ZERO_ADDR and v2Owners[i] is truthy before pushing into
tokensAndOwners so sumTokens receives only valid [token, owner] tuples (refer to
v2Owners, v2MorphoVaults, ZERO_ADDR, tokensAndOwners and the code path that
calls sumTokens).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@projects/surf-liquid/index.js`:
- Around line 71-77: The adapter metadata currently exports module.exports with
base: { tvl, staking } but does not mark that base.tvl reports
wrapped/downstream Morpho ERC-4626 share balances; update the exported metadata
to explicitly flag this by adding doublecounted: true and misrepresentedTokens:
true (or alternatively unwrap the vault shares to underlying assets) so
consumers know the TVL may overlap with underlying protocols; locate the
module.exports block (symbols: module.exports, base, tvl, staking) and add those
two boolean flags to the metadata object.
- Around line 25-27: The loop that builds tokensAndOwners currently only checks
v2MorphoVaults[i] !== ZERO_ADDR and can push invalid pairs when v2Owners[i] or
v2MorphoVaults[i] is falsy; update the guard in the loop that iterates over
v2Owners to ensure both v2MorphoVaults[i] is truthy and not ZERO_ADDR and
v2Owners[i] is truthy before pushing into tokensAndOwners so sumTokens receives
only valid [token, owner] tuples (refer to v2Owners, v2MorphoVaults, ZERO_ADDR,
tokensAndOwners and the code path that calls sumTokens).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 736dc5ba-3ccc-4d6a-91fd-fbfec4ee2caa

📥 Commits

Reviewing files that changed from the base of the PR and between e90db2a and ccfb280.

📒 Files selected for processing (1)
  • projects/surf-liquid/index.js

- Mark adapter as doublecounted and misrepresentedTokens since TVL counts
  Morpho ERC-4626 vault shares (vault-in-vault)
- Harden V2 loop to check both owner and vault are truthy before pushing
@llamabutler
Copy link
Copy Markdown

The adapter at projects/surf-liquid exports TVL:

base                      195.86 k
base-staking              37.10 k
staking                   37.10 k

total                    195.86 k 

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
projects/surf-liquid/index.js (1)

37-37: Consider documenting the hardcoded fromBlock.

The magic number 38856207 should have a brief inline comment explaining it's the V3 factory deployment block, improving maintainability for future developers.

-    fromBlock: 38856207,
+    fromBlock: 38856207, // V3 factory deployment block
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@projects/surf-liquid/index.js` at line 37, The literal 38856207 assigned to
fromBlock is a magic number; add a brief inline comment next to the fromBlock
assignment (the "fromBlock" property containing 38856207) stating that this is
the V3 factory deployment block (include network and approximate date if known)
so future maintainers understand why it is hardcoded and can update it safely.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@projects/surf-liquid/index.js`:
- Line 37: The literal 38856207 assigned to fromBlock is a magic number; add a
brief inline comment next to the fromBlock assignment (the "fromBlock" property
containing 38856207) stating that this is the V3 factory deployment block
(include network and approximate date if known) so future maintainers understand
why it is hardcoded and can update it safely.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7cd87ec0-142d-4205-a8cd-adaf1c88b72a

📥 Commits

Reviewing files that changed from the base of the PR and between ccfb280 and 241a0d7.

📒 Files selected for processing (1)
  • projects/surf-liquid/index.js

@llamabutler
Copy link
Copy Markdown

The adapter at projects/surf-liquid exports TVL:

base                      213.04 k
base-staking              38.31 k
staking                   38.31 k

total                    213.04 k 

@RohanNero RohanNero merged commit 1177f02 into DefiLlama:main Mar 27, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants